﻿using UnityEngine;
using System.Text;
using System.Collections.Generic;
using System;

public class ControllerObject : MonoBehaviour
{
    public float MoveSpeedFactor = 15f;
    public float MoveStopFactor = 0.01f;
    public float LerpSpeedFactor = 5f;
    public float LerpDistanceFactor = 8f;
    public float LerpMaxDistance = 1f;
    public float zoomMax = 42;
    public float zoomMin = 32;
    public float zoomSpeedFactor = 15f;
    public float zoomBounceFactor = 10f;
    public float zoomBounceSpeedFactor = 5f;
    public float Dis2Fov = 60f;
    public float Scroll2Fov = 50f;
    public float FingerEndDiffTime = 0.1f;


    #region 私有变量
    private float fingerEndTime;
    private float moveSpeed;
    private float zoomSpeed;
    private float zoomDiff;
    private float StartFov = 0f;
    private float CurFov { get { return StartFov + zoomDiff; } }
    private Vector2 CurFingerPos = Vector2.zero;
    private Vector3 CurPointPos = Vector3.zero;
    private Vector3 StartPointPos = Vector3.zero;

    private Camera ctrlCamera;
    private Transform ctrlCameraTran;
    private PlatformController controller;
    #endregion

    #region Constant  Static 常量
    const float MaxRayCastDis = 1000f;
    static int TerrainLayer;
    #endregion

    #region 调试信息
    private IList<string> textList = new List<string>();

    public class TouchInfo
    {
        public TouchPhase phase;
        public int count;
        public int id;
        public int index1;
        public int index2;
    }
    List<TouchInfo> touchInfoLst = new List<TouchInfo>();
    #endregion

    void Awake()
    {
        TerrainLayer = 1 << LayerMask.NameToLayer("terrain");
        ctrlCamera = GetComponent<Camera>();
        if (ctrlCamera != null)
        {
            ctrlCameraTran = ctrlCamera.transform;
        }

#if UNITY_STANDALONE_WIN || UNITY_EDITOR
        controller = this.gameObject.AddComponent<MouseController>();
#else
        controller = this.gameObject.AddComponent<TouchController>();
#endif

        controller.Init(
            OnBeginCallback,
            OnMoveCallback,
            OnScaleCallback,
            OnUpdateCallback,
            OnEndCallback,
            OnEndScaleCallback,
            OnMulti2OneCallback,
            OnOne2MultiCallback,
            ctrlCamera);
    }

    private void OnBeginCallback(Vector2 pos, int id)
    {
        StartPointPos = GetHitPoint(pos);
        CurFingerPos = pos;
        moveSpeed = MoveSpeedFactor;
    }

    private void OnMoveCallback(Vector2 pos, int id)
    {
        CurFingerPos = pos;
    }

    private void OnEndCallback(Vector2 vec, int id)
    {
        //LogMark(string.Format("fingerID:{0},DeltaPostion:{1},viewPortDelta:{2}", id, pos, view));
        if(Time.time - fingerEndTime > FingerEndDiffTime)
        {
            CurFingerPos += Vector2.ClampMagnitude(vec * LerpDistanceFactor,LerpMaxDistance);
        }
        moveSpeed = LerpSpeedFactor;
    }

    private void OnBeginScaleCallback()
    {
        zoomDiff = 0f;
    }

    private void OnScaleCallback(float deltaDis)
    {
        if (ctrlCamera == null)
        {
            return;
        }

        if (deltaDis == 0)
        {
            return;
        }
#if UNITY_STANDALONE_WIN || UNITY_EDITOR
        float diff = deltaDis * Scroll2Fov;
#else 
        OnBeginCallback(GetHitPoint(new Vector2(0.5f, 0.5f)), 0);
        float diff = deltaDis * Dis2Fov;
#endif

        if (zoomDiff == 0 || Mathf.Sign(zoomDiff) != Mathf.Sign(deltaDis))
        {
            zoomDiff = diff; 
            StartFov = ctrlCamera.fieldOfView;
        }
        else
        {
            zoomDiff += diff;
        }        

        zoomSpeed = zoomSpeedFactor;
    }

    private void OnEndScaleCallback(float time)
    {
        if(ctrlCamera == null)
        {
            return;
        }

        fingerEndTime = time;

        if (ctrlCamera.fieldOfView > zoomMax)//已经超出正常范围了，弹回去
        {
            StartFov = ctrlCamera.fieldOfView;
            zoomDiff = zoomMax - StartFov;
            zoomSpeed = zoomBounceSpeedFactor;
        }else if(ctrlCamera.fieldOfView < zoomMin)//已经超出正常范围了，弹回去
        {
            StartFov = ctrlCamera.fieldOfView;
            zoomDiff = zoomMin - StartFov;
            zoomSpeed = zoomBounceSpeedFactor;
        }
        else if(CurFov > zoomMax)//目标位置超出正常范围，限制一下
        {
            zoomDiff = zoomMax - StartFov;
        }else if(CurFov < zoomMin)//目标位置超出正常范围，限制一下
        {
            zoomDiff = zoomMin - StartFov;
        }
    }

    private void OnUpdateCallback()
    {
        if (ctrlCameraTran == null || ctrlCamera == null)
        {
            return;
        }

        CurPointPos = GetHitPoint(CurFingerPos);
        if(Vector3.Distance(CurPointPos,StartPointPos) > MoveStopFactor)
        {
            Vector3 diff = StartPointPos - CurPointPos;
            ctrlCameraTran.position = Vector3.Lerp(
                            ctrlCameraTran.position,
                            ctrlCameraTran.position + diff,
                            Time.deltaTime * moveSpeed);
        }

        if(zoomDiff != 0)
        {
            //float bound = zoomDiff > 0 ? zoomMax + zoomBounceFactor : zoomMin - zoomBounceFactor;
            float targetFov = Mathf.Clamp(
                            CurFov, 
                            zoomMin - zoomBounceFactor, 
                            zoomMax + zoomBounceFactor);
            ctrlCamera.fieldOfView = Mathf.Lerp(
                            ctrlCamera.fieldOfView,
                            targetFov,
                            Time.deltaTime * zoomSpeed);

            if (Mathf.Approximately(ctrlCamera.fieldOfView, targetFov))
            {
                zoomDiff = 0;
                ctrlCamera.fieldOfView = targetFov;
            }
        }
    }

    private void OnOne2MultiCallback()//无用
    {

    }

    private Vector3 GetHitPoint(Vector3 viewPos)
    {
        if (ctrlCamera != null)
        {
            Ray ray = ctrlCamera.ViewportPointToRay(viewPos);
            RaycastHit hit;
            bool cast = Physics.Raycast(ray, out hit, MaxRayCastDis, TerrainLayer);
            if (cast == true)
            {
               return  hit.point;
            }
        }
        return Vector3.zero;
    }

    #region 调试区
    private void OnMulti2OneCallback(int fingerID, int index1,int index2, TouchPhase phase)
    {
        Predicate<TouchInfo> predicate = delegate (TouchInfo info)
        {
            return info.id == fingerID;
        };
        int index = touchInfoLst.FindIndex(predicate);
        if (index == -1)
        {
            TouchInfo info = new TouchInfo();
            info.count = 1;
            info.phase = phase;
            info.index1 = index1;
            info.index2 = index2;
            info.id = fingerID;
            touchInfoLst.Add(info);
        }
        else
        {
            if(phase == TouchPhase.Ended)
            {
                //touchInfoLst.RemoveAt(index);
                //return;
            }
            TouchInfo info = touchInfoLst[index];
            info.phase = phase;
            info.index1 = index1;
            info.index2 = index2;

        }
    }

    private string GetText()
    {
        StringBuilder stringBuilder = new StringBuilder();

        foreach (string text in this.textList)
        {
            stringBuilder.Append(text);
            stringBuilder.Append("\n");
        }

        return stringBuilder.ToString();
    }

    private void LogMark(string text)
    {
        if (this.textList.Count > 10)
        {
            this.textList.RemoveAt(0);
        }

        textList.Add(text);
    }

    static GUIStyle style = new GUIStyle();
    private void OnGUI()
    {
        style.fontSize = 40;
        for (int i = 0; i < touchInfoLst.Count; i++)
        {
            TouchInfo info = touchInfoLst[i];
            GUI.Label(
                new Rect(20, i * 50, 400, 50), 
                string.Format("TouchId:{0},Index1:{1},Phase:{2}",info.id, info.index1,info.phase), 
                style);
        }

        for (int i = 0; i < textList.Count; i++)
        {
            GUI.Label(
                new Rect(20,(i + touchInfoLst.Count) * 50,400,50),
                textList[i],
                style);
        }

        GUI.Label(new Rect(20, 300, 400, 50), string.Format("moveSpeed:{0}", moveSpeed), style);
        GUI.Label(new Rect(20, 350, 400, 50), string.Format("zoomSpeed:{0}", zoomSpeed), style);
        GUI.Label(new Rect(20, 400, 400, 50), string.Format("StartFov:{0}", StartFov), style);
        GUI.Label(new Rect(20, 450, 400, 50), string.Format("zoomDiff:{0}", zoomDiff), style);
        GUI.Label(new Rect(20, 500, 400, 50), string.Format("CurFov:{0}", CurFov), style);
        GUI.Label(new Rect(20, 550, 400, 50), string.Format("RealFov:{0}", ctrlCamera.fieldOfView), style);
    }
    #endregion
}